﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;

namespace Galaktika.BI.Addin.VisifireChart.Runtime.PivotChart
{
    /// <summary>
    /// Класс, описывающий точку области данных сводной таблицы
    /// </summary>
    public class DataPointInfo
    {
        private IList<String> m_CaptionParts = null;
        /// <summary>
        /// Заголовки для данных (Заголовки элементов из области строк для конкретного значения)
        /// </summary>
        public IList<String> CaptionParts
        {
            get
            {
                return m_CaptionParts;
            }
        }

        private IList<String> m_UniqueNameParts = null;
        /// <summary>
        /// Уникальные имена для данных (Уникальные имена элементов из области строк для конкретного значения)
        /// </summary>
        public IList<String> UniqueNameParts
        {
            get
            {
                return m_UniqueNameParts;
            }
        }

        /// <summary>
        /// Заголовки для данных (перечисляются через ".")
        /// </summary>
        public String Captions
        {
            get
            {
                String ret = null;
                foreach (String str in CaptionParts)
                {
                    if (ret != null)
                        ret = ret + ".";
                    ret = ret + str;
                }
                return ret;
            }
        }

        /// <summary>
        /// Уникальные имена для данных (перечисляются через ".")
        /// </summary>
        public String UniqueNames
        {
            get
            {
                String ret = null;
                foreach (String str in UniqueNameParts)
                {
                    if (ret != null)
                        ret = ret + ".";
                    ret = ret + str;
                }
                return ret;
            }
        }

        private double m_Value = 0.0;
        /// <summary>
        /// Значение
        /// </summary>
        public double Value
        {
            get
            {
                return m_Value;
            }
        }

        private String m_ColumnName = String.Empty;
        /// <summary>
        /// Название колонки сводной таблицы
        /// </summary>
        public String ColumnName
        {
            get
            {
                return m_ColumnName;
            }
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="val"></param>
        /// <param name="name"></param>
        public DataPointInfo(double val, String name)
        {
            m_Value = val;
            m_ColumnName = name;
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="val"></param>
        /// <param name="name"></param>
        public DataPointInfo(double val, String name, IList<String> captions, IList<String> uniqueNames)
            :
            this(val, name)
        {
            m_CaptionParts = captions;
            m_UniqueNameParts = uniqueNames;
        }
    }

    /// <summary>
    /// Информация о данных для одной серии
    /// </summary>
    public class SeriesInfo
    {
        /// <summary>
        /// Возвращает массив данных столбца сводной таблицы
        /// </summary>
        public double[] SeriesData
        {
            get
            {
                if (DataPointsInfo == null || DataPointsInfo.Count == 0)
                {
                    return null;
                }

                double[] data = new double[DataPointsInfo.Count];

                for (int i = 0; i < DataPointsInfo.Count; i++)
                {
                    DataPointInfo dpi = DataPointsInfo[i];
                    data[i] = dpi != null ? dpi.Value : 0.0;
                }
                return data;
            }
        }

        /// <summary>
        /// Возвращает заголовки для данных
        /// </summary>
        public String[] SeriesCaptions
        {
            get
            {
                if (DataPointsInfo == null || DataPointsInfo.Count == 0)
                {
                    return null;
                }

                String[] capt = new String[DataPointsInfo.Count];
                for (int i = 0; i < DataPointsInfo.Count; i++)
                {
                    DataPointInfo dpi = DataPointsInfo[i];
                    capt[i] = dpi != null ? dpi.Captions : String.Empty;
                }
                return capt;
            }
        }

        /// <summary>
        /// Длинна (количество данных)
        /// </summary>
        public int DataLength
        {
            get
            {
                if (DataPointsInfo == null)
                    return 0;
                else
                    return DataPointsInfo.Count;
            }
        }

        /// <summary>
        /// Название колонки сводной таблицы
        /// </summary>
        public String SeriesName
        {
            get
            {
                if (DataPointsInfo != null && DataPointsInfo.Count > 0)
                {
                    return DataPointsInfo[0].ColumnName;
                }
                return String.Empty;
            }
        }

        #region Максимальное и минимальное значение
        private double m_MaxValue = 0.0;
        /// <summary>
        /// Максимальное значение данных из SeriesData
        /// </summary>
        public double MaxValue
        {
            get
            {
                if (m_NeedCalculateMinMax)
                    CalculateMinMax();
                return m_MaxValue;
            }
        }

        private double m_MinValue = 0.0;
        /// <summary>
        /// Минимальное значение данных из SeriesData
        /// </summary>
        public double MinValue
        {
            get
            {
                if (m_NeedCalculateMinMax)
                    CalculateMinMax();
                return m_MinValue;
            }
        }

        /// <summary>
        /// Флаг необходимости пересчета минимума и максимума
        /// </summary>
        private bool m_NeedCalculateMinMax = true;

        /// <summary>
        /// Вычисляем максимальное и минимальное значение
        /// </summary>
        private void CalculateMinMax()
        {
            foreach (DataPointInfo dpi in DataPointsInfo)
            {
                if (dpi.Value > m_MaxValue)
                    m_MaxValue = dpi.Value;
                if (dpi.Value < m_MinValue)
                    m_MinValue = dpi.Value;
            }
            m_NeedCalculateMinMax = false;
        }

        #endregion Максимальное и минимальное значение

        #region Фильтрация данных с учетом индексов
        private IndiciesManager m_IndicesManager;
        private IndiciesManager Indicies
        {
            get
            {
                if (m_IndicesManager == null)
                {
                    m_IndicesManager = new IndiciesManager(this.DataIndices);
                }

                return m_IndicesManager;
            }
        }

        private IList<T> FilterByIndices<T>(IList<T> input)
        {
            List<T> res = new List<T>();
            int i = 0;
            foreach (T item in input)
            {
                if (this.Indicies.IsAllowed(i++))
                {
                    res.Add(item);
                }
            }

            return res;
        }
        #endregion Фильтрация данных с учетом индексов

        private IList<DataPointInfo> m_FilteredDataPointsInfo = null;
        private IList<DataPointInfo> m_RealDataPointsInfo = null;
        /// <summary>
        /// Информация о элементах колонки сводной таблицы (отфильтрована с учетом DataIndices)
        /// </summary>
        public IList<DataPointInfo> DataPointsInfo
        {
            get
            {
                if (m_FilteredDataPointsInfo == null)
                {
                    m_FilteredDataPointsInfo = new List<DataPointInfo>();
                }

                //Фильруем если есть необходимость и есть что фильтровать
                if (m_RealDataPointsInfo != null && m_NeedReFilterDataPoints)
                {
                    m_FilteredDataPointsInfo = this.FilterByIndices<DataPointInfo>(m_RealDataPointsInfo);
                    m_NeedReFilterDataPoints = false;
                }

                return m_FilteredDataPointsInfo;
            }
        }

        private IList<DataPointInfo> RealDataPointsInfo
        {
            get
            {
                if (m_RealDataPointsInfo == null)
                {
                    m_RealDataPointsInfo = new List<DataPointInfo>();
                }
                return m_RealDataPointsInfo;
            }
        }

        /// <summary>
        /// Флаг необходимости обновления отфильтрованных данных
        /// </summary>
        private bool m_NeedReFilterDataPoints = true;

        private String m_DataIndices = String.Empty;
        /// <summary>
        /// Массив допустимых индексов данных (строк источника).
        /// </summary>
        public String DataIndices
        {
            get
            {
                return m_DataIndices;
            }
            set
            {
                m_DataIndices = value;
                m_NeedReFilterDataPoints = true;
            }
        }


        public void SetData(double[] seriesData, String seriesName)
        {
            m_RealDataPointsInfo = new List<DataPointInfo>();
            for (int i = 0; i < seriesData.Length; i++)
            {
                DataPointInfo dpi = new DataPointInfo(seriesData[i], seriesName);
                m_RealDataPointsInfo.Add(dpi);
                //Сразу вычисляем максимальное и минимальное значение
                if (dpi.Value > m_MaxValue)
                    m_MaxValue = dpi.Value;
                if (dpi.Value < m_MinValue)
                    m_MinValue = dpi.Value;
            }
        }

        public void AddDataPoint(DataPointInfo dpi)
        {
            RealDataPointsInfo.Add(dpi);
            m_NeedCalculateMinMax = true;
        }

        public SeriesInfo()
        {

        }
    }

    public class IndiciesManager
    {
        #region Resolver
        private abstract class Resolver
        {
            public Resolver()
            {
            }

            public abstract bool IsAllowed(int index);
        }

        private class SingleValueResolver : Resolver
        {
            private int m_Value;
            public SingleValueResolver(string value)
            {
                int.TryParse(value, out m_Value);
            }

            public override bool IsAllowed(int index)
            {
                return m_Value == index;
            }
        }

        private class RangeResolver : Resolver
        {
            private int m_Left;
            private int m_Right;
            public RangeResolver(string left, string right)
            {
                int.TryParse(left, out m_Left);
                int.TryParse(right, out m_Right);
            }

            public override bool IsAllowed(int index)
            {
                return (index >= m_Left) && (index <= m_Right);
            }
        }

        private class DefaultResolver : Resolver
        {
            public DefaultResolver()
            {
            }

            public override bool IsAllowed(int index)
            {
                return true;
            }
        }
        #endregion

        private List<Resolver> m_ResolveChain;
        public IndiciesManager(string indicies)
        {
            m_ResolveChain = this.CreateResolveChain(indicies);
        }

        private List<Resolver> CreateResolveChain(string indicies)
        {
            List<Resolver> res = new List<Resolver>();
            string[] parts = indicies.Trim().Split(',');
            for (int i = 0; i < parts.Length; i++)
            {
                if (string.IsNullOrEmpty(parts[i])) continue;
                string[] values = parts[i].Split('-');
                switch (values.Length)
                {
                    case 1:
                        if (!string.IsNullOrEmpty(values[0]))
                        {
                            res.Add(new SingleValueResolver(values[0]));
                        }
                        break;
                    case 2:
                        if (!string.IsNullOrEmpty(values[0]) && !string.IsNullOrEmpty(values[1]))
                        {
                            res.Add(new RangeResolver(values[0], values[1]));
                        }
                        break;
                    default:
                        break;
                }
            }

            if (res.Count == 0)
            {
                res.Add(new DefaultResolver());
            }

            return res;
        }

        public bool IsAllowed(int index)
        {
            bool res = false;
            foreach (Resolver resolver in m_ResolveChain)
            {
                if (resolver.IsAllowed(index))
                {
                    res = true;
                    break;
                }
            }
            return res;
        }
    }
}
